#ifndef PERMUTACIONCPU_CU
#define PERMUTACIONCPU_CU

#include "permutacionCPU.cuh"
#include "../fitness/fitnessPermutacion.cu"

/******************* Implementacion de Funciones del  *****************/

 void inicializarPoblacionPermutacionCPU(int* poblacion, float* fitnessActual, MersenneTwister *mt, int cantCromosomasPoblacion, float* fitnessOrdenadoHostCPU, float* matrizCostosDevice){
	//Obtengo posicion a escribir.
	int posicionCromosoma = 0;
	int posicionFitness = 0;

	//inicializo la poblacion en -1
	for(int i = 0; i<cantGenesEnCromosoma * cantidadPoblacionesCPU * cantCromosomasPoblacion; i++){
		poblacion[posicionCromosoma+i] = -1;
	}

	for(int i = 0; i< cantidadPoblacionesCPU * cantCromosomasPoblacion; i++){

		for(int j = 0; j<cantGenesEnCromosoma; j++){
			//Genero valor aleatorio.
			unsigned int r = (mt->genrand_int32()% cantGenesEnCromosoma);
			while(poblacion[posicionCromosoma + r] != -1){
				r = (r+1) % cantGenesEnCromosoma;
			}
			poblacion[posicionCromosoma+r] = j;
		}
		posicionCromosoma = posicionCromosoma + cantGenesEnCromosoma;
	}

	posicionCromosoma = 0;
	for(int i = 0; i< cantidadPoblacionesCPU * cantCromosomasPoblacion; i++){
		fitnessActual[posicionFitness] = calcularFitnessPermutacionCPU(poblacion, posicionCromosoma, matrizCostosDevice);
		fitnessOrdenadoHostCPU[posicionFitness] = fitnessActual[posicionFitness];
		posicionFitness++;
		posicionCromosoma = posicionCromosoma + cantGenesEnCromosoma;
	}
}



 void mainKernelPermutacionCPU(int* poblacionActual, int* nuevaPoblacion, float* fitnessActual, float* nuevoFitness, int * poblacionActualAcambiar, int *  nuevaPoblacionAcambiar, int* individuosAMigrar, MersenneTwister *mt, int nroPoblacion, int posActual, int *individuoAMigrarCPUaGPUhost,  float* fitnessOrdenado, int* vectorIndicesFitnessOrdenado, int iteracion, float* probabilidadRuleta, float* fitnessTotalParaRuleta, int* mejorIndividuo, float* mejorFitnessIndividuoCPU, float* matrizCostosHost){
	 if(tipoSeleccion == 2){
		asignarProbabilidadRuletaPermutacionCPU(fitnessActual, probabilidadRuleta, fitnessTotalParaRuleta, mt, nroPoblacion, posActual);
	}

	int posicionCromosoma1 = seleccionPermutacionCPU(fitnessActual, mt, tipoSeleccion, nroPoblacion, posActual, probabilidadRuleta, vectorIndicesFitnessOrdenado);
	int posicionCromosoma2 = seleccionPermutacionCPU(fitnessActual, mt, tipoSeleccion, nroPoblacion, posActual, probabilidadRuleta, vectorIndicesFitnessOrdenado);

	cruzamientoRepresentacionPermutacionCPU(poblacionActual, nuevaPoblacion, posicionCromosoma1, posicionCromosoma2, probabilidadCruzamiento, mt, tipoCruzamiento, nroPoblacion, posActual);
	mutacionPermutacionCPU(nuevaPoblacion, probabilidadMutacion, mt, tipoMutacion, nroPoblacion, posActual);

	asignarFitnessPermutacionCPU(nuevaPoblacion, nuevoFitness, nroPoblacion, posActual, fitnessOrdenado, matrizCostosHost);
	reemplazoPermutacionCPU(poblacionActual, nuevaPoblacion, poblacionActualAcambiar, nuevaPoblacionAcambiar, fitnessActual, nuevoFitness, tipoReemplazo, nroPoblacion, posActual);

	if(posActual == cantCromosomasPoblacion/2 -1){
		if((generacionesMigrar > 0) && ((iteracion + 1) % generacionesMigrar == 0)){
			//si posActual(numero hilo) es el ultimo del bloque
			individuosAMigrarRepresentacionPermutacionCPU(nuevoFitness, nuevaPoblacion, individuosAMigrar, nroPoblacion, posActual, individuoAMigrarCPUaGPUhost);
		}
		if(imprimirPoblacionEntera == 0){
			//obtengo el mejor individuo
			int posMejor = obtenerMejorIndividuoPermutacion(nroPoblacion, nuevoFitness);
			for(int i = 0; i < cantGenesEnCromosoma; i++){
				mejorIndividuo[i + nroPoblacion * cantGenesEnCromosoma] = nuevaPoblacion[posMejor+i];
			}
			//asignar mejor fitness
			mejorFitnessIndividuoCPU[nroPoblacion] = nuevoFitness[posMejor/cantGenesEnCromosoma];
		}
	}
	//se pasa el individuo a migrar si hay al menos una poblacion en la gpu y soy la ultima poblacion de la cpu
}


void obtenerMejoresIndividuosPermutacionCPU(int* poblacion, float* fitness, int* mejorIndividuo, float* mejorFitnessIndividuoCPU){
 	//obtengo el mejor individuo
	for(int nroPoblacion = 0; nroPoblacion < cantidadPoblacionesCPU; nroPoblacion++){
		int posMejor = obtenerMejorIndividuoPermutacion(nroPoblacion, fitness);
		for(int i = 0; i < cantGenesEnCromosoma; i++){
			mejorIndividuo[nroPoblacion * cantGenesEnCromosoma + i] = poblacion[posMejor+i];
		}
		//asignar mejor fitness
		mejorFitnessIndividuoCPU[nroPoblacion] = fitness[posMejor/cantGenesEnCromosoma];
	}
 }
 
 /******************* Implementacion de Funciones de Migracion *****************/

  //Obtiene el mejor individuo de una poblacion (1 bloque por poblacion)
  //selecciona el mejor elemento
int obtenerMejorIndividuoPermutacion(int nroPoblacion, float* vectorFitness){
	float mejor = INT_MIN;
  	int indiceMejor;
  	float valorFitness;
  	int indiceMemoria = nroPoblacion * cantCromosomasPoblacion;
  	for(int i = 0; i< cantCromosomasPoblacion; i++){
  		valorFitness = vectorFitness[indiceMemoria+i];
  		if(valorFitness > mejor){
  			mejor = valorFitness;
  			indiceMejor = i;
  		}
  	}
  	int posicionCromosomaMejor = nroPoblacion * cantCromosomasPoblacion* cantGenesEnCromosoma+ indiceMejor * cantGenesEnCromosoma;
  	return posicionCromosomaMejor;
  }

  //Obtiene el indice del peor individuo y del fitness del peor individuo de una poblacion (1 bloque por poblacion) y retorna por referencia en el arreglo indices
 void obtenerPeorIndividuoPermutacion(int nroPoblacion,float* vectorFitness, int indices[2]){
	float peor = INT_MAX;
  	int indicePeor;
  	float valorFitness;
  	int posicionCromosomaPeor;
  	int indiceMemoria = nroPoblacion * cantCromosomasPoblacion;
  	for(int i = 0; i< cantCromosomasPoblacion; i++){
  		valorFitness = vectorFitness[indiceMemoria+i];
  		if(valorFitness < peor){
  			peor = valorFitness;
  			indicePeor = i;
  		}
  	}
  	posicionCromosomaPeor = nroPoblacion * cantCromosomasPoblacion * cantGenesEnCromosoma + indicePeor * cantGenesEnCromosoma;
  	indices[0] = posicionCromosomaPeor;
  	indices[1] = indicePeor;
  }

 void migracionRepresentacionPermutacionCPU(int* poblacionActual, float* fitnessActual, int* individuosAMigrar, MersenneTwister *mt, int* individuoAMigrarGPUaCPUhost, float* matrizCostosHost){
 	unsigned int r = mt->genrand_int32() % cantCromosomasPoblacion;
 	int indices[2];
 	int posicionCromosomaEmigrante;
 	int posicionCromosomaInmigrante;
 	int posicionFitness;
 	int aux;
 	for(int bloq = 0; bloq < cantidadPoblacionesCPU; bloq++){
 		if(bloq == 0){
			aux = cantidadPoblacionesCPU - 1;
		}else{
			aux = (bloq - 1) % cantidadPoblacionesCPU;
		}
		posicionCromosomaInmigrante = aux * cantGenesEnCromosoma;
 		obtenerPeorIndividuoPermutacion(bloq, fitnessActual, indices);

 		posicionCromosomaEmigrante = indices[0];
 		if(bloq == 0 && cantidadPoblacionesGPU > 0){
 			for(int i = 0; i<cantGenesEnCromosoma; i++){
 				poblacionActual[posicionCromosomaEmigrante+i] = individuoAMigrarGPUaCPUhost[i];
 			}
 		}else{
 			for(int i = 0; i<cantGenesEnCromosoma; i++){
 				poblacionActual[posicionCromosomaEmigrante+i] = individuosAMigrar[posicionCromosomaInmigrante+i];
 			}
 		}
 		posicionFitness = bloq * cantCromosomasPoblacion + indices[1];
 		fitnessActual[posicionFitness] = calcularFitnessPermutacionCPU(poblacionActual, posicionCromosomaEmigrante, matrizCostosHost);
 	}
 }

void individuosAMigrarRepresentacionPermutacionCPU(float* nuevoFitness, int* nuevaPoblacion, int* individuosAMigrar, int nroPoblacion, int posActual, int *individuoAMigrarCPUaGPUhost){
 	int posicionMejorIndividuo = obtenerMejorIndividuoPermutacion(nroPoblacion, nuevoFitness);
 	if(cantidadPoblacionesGPU > 0 && nroPoblacion == cantidadPoblacionesCPU-1){
 		for(int i = 0; i<cantGenesEnCromosoma; i++){
 			individuoAMigrarCPUaGPUhost[i] = nuevaPoblacion[posicionMejorIndividuo+i];
 		}
 	}else{
 		for(int i = 0; i<cantGenesEnCromosoma; i++){
 			individuosAMigrar[nroPoblacion*cantGenesEnCromosoma + i] = nuevaPoblacion[posicionMejorIndividuo+i];
 		}
 	}
 }



/******************* Implementacion de Funciones de Selecccion *****************/

void asignarProbabilidadRuletaPermutacionCPU(float* fitnessActual, float* probabilidadRuletaPermutacion, float* fitnessTotalParaRuleta,  MersenneTwister *mt, int nroPoblacion, int posActual){
	if(posActual == 0){
		fitnessTotalParaRuleta[nroPoblacion] = 0;
		for(int i = nroPoblacion*cantCromosomasPoblacion; i < nroPoblacion*cantCromosomasPoblacion + cantCromosomasPoblacion; i++){
			fitnessTotalParaRuleta[nroPoblacion] += fitnessActual[i];
		}

		for(int i = nroPoblacion*cantCromosomasPoblacion; i < nroPoblacion*cantCromosomasPoblacion + cantCromosomasPoblacion; i++){
			probabilidadRuletaPermutacion[i] = fitnessActual[i]/fitnessTotalParaRuleta[nroPoblacion] * 100;
		}
	}
}

 int seleccionPermutacionCPU(float* vectorFitness, MersenneTwister *mt, int ejecutarFuncion, int nroPoblacion, int posActual, float* probabilidadRuleta, int* vectorIndicesFitnessOrdenado){
	int resultado = -1;
	if(ejecutarFuncion == 0){
		resultado = seleccionPorRangoPermutacionCPU(vectorIndicesFitnessOrdenado, mt, nroPoblacion, posActual);
	}
	else{
		if(ejecutarFuncion == 1){
			resultado = seleccionPorTorneoPermutacionCPU(vectorFitness, mt, nroPoblacion, posActual);
		}
		else{
			if(ejecutarFuncion == 2){
				resultado = seleccionRuedaRuletaPermutacionCPU(probabilidadRuleta, mt, nroPoblacion, posActual);
			}
		}
	}
	return resultado;
}


//Seleccion por torneo
//selecciona 2 individuos al azar y devuelve la posicion del mejor
 int seleccionPorTorneoPermutacionCPU(float* vectorFitness, MersenneTwister *mt, int nroPoblacion, int posActual){
	unsigned int r = mt->genrand_int32()% cantCromosomasPoblacion;
	int posicionFitnessAleatoria1 = nroPoblacion * cantCromosomasPoblacion + r;
	int posicionCromosomaAleatoria1 = nroPoblacion * cantCromosomasPoblacion * cantGenesEnCromosoma + r * cantGenesEnCromosoma;
	r = mt->genrand_int32()% cantCromosomasPoblacion;
	int posicionFitnessAleatoria2 = nroPoblacion * cantCromosomasPoblacion + r;
	int posicionCromosomaAleatoria2 = nroPoblacion * cantCromosomasPoblacion * cantGenesEnCromosoma + r * cantGenesEnCromosoma;
	if(vectorFitness[posicionFitnessAleatoria1] > vectorFitness[posicionFitnessAleatoria2])
		return posicionCromosomaAleatoria1;
	return posicionCromosomaAleatoria2;
}


 //Seleccion por rango
  int seleccionPorRangoPermutacionCPU(int* vectorIndicesFitnessOrdenado, MersenneTwister *mt, int nroPoblacion, int posActual){
 	unsigned int indiceMejor =  mt->genrand_int32()% cantidadIndividuosSeleccionPorRango;
  	int posicionCromosoma = nroPoblacion * cantCromosomasPoblacion * cantGenesEnCromosoma + vectorIndicesFitnessOrdenado[nroPoblacion * cantCromosomasPoblacion + indiceMejor] * cantGenesEnCromosoma;
 	return posicionCromosoma;
 }

 //Seleccion por rueda de ruleta
int seleccionRuedaRuletaPermutacionCPU(float* probabilidadRuleta, MersenneTwister *mt, int nroPoblacion, int posActual){
	float aux =  mt->genrand_int32();
 	float r = fmod(aux, 100);
 	float suma = probabilidadRuleta[nroPoblacion*cantCromosomasPoblacion];
 	int i = 0;
 	while((r > suma) && (i < cantCromosomasPoblacion - 1)){
 		i++;
 		suma = suma + probabilidadRuleta[nroPoblacion * cantCromosomasPoblacion + i];
 	}
	int posicionCromosoma = nroPoblacion * cantCromosomasPoblacion * cantGenesEnCromosoma + i * cantGenesEnCromosoma;
	return posicionCromosoma;
}


/******************* Implementacion de Funciones de Cruzamiento *****************/


 void cruzamientoRepresentacionPermutacionCPU(int* poblacionActual, int* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, float probabilidad, MersenneTwister *mt, int ejecutarFuncion, int nroPoblacion, int posActual){
	if(ejecutarFuncion == 0){
		cruzamientoCromosomaPMX(poblacionActual, nuevaPoblacion, posicionCromosoma1, posicionCromosoma2, probabilidad, mt, nroPoblacion, posActual);
	}else{
		if(ejecutarFuncion == 1){
			cruzamientoCromosomaOX(poblacionActual, nuevaPoblacion, posicionCromosoma1, posicionCromosoma2, probabilidad, mt, nroPoblacion, posActual);
		}else{
			if(ejecutarFuncion == 2){
				cruzamientoCromosomaCX(poblacionActual, nuevaPoblacion, posicionCromosoma1, posicionCromosoma2, probabilidad, mt, nroPoblacion, posActual);
			}
		}
	}
}

 void cruzamientoCromosomaPMX(int* poblacionActual, int* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, float probabilidad, MersenneTwister *mt, int nroPoblacion, int posActual){
	int posicionNuevoCromosoma1 = posActual * cantGenesEnCromosoma * 2 + nroPoblacion * totalGenesEnPoblacion;
	int posicionNuevoCromosoma2 = posicionNuevoCromosoma1 + cantGenesEnCromosoma;

	unsigned int prob;
	prob = (mt->genrand_int32() % 101);
	if(prob < probabilidad ){
		unsigned int r1 = mt->genrand_int32() % cantGenesEnCromosoma;
		unsigned int r2 = mt->genrand_int32() % cantGenesEnCromosoma;
		if(r2 == r1){
			r2 = (r2 + 1) % cantGenesEnCromosoma;
		}

		if(r2<r1){
			unsigned int r3 = r1;
			r1 = r2;
			r2 = r3;
		}

		for(int j=0; j<cantGenesEnCromosoma; j++){
			if((j>=r1) && (j <= r2)){
				nuevaPoblacion[posicionNuevoCromosoma1+j] = poblacionActual[posicionCromosoma1+j];
				nuevaPoblacion[posicionNuevoCromosoma2+j] = poblacionActual[posicionCromosoma2+j];
			}else{
				nuevaPoblacion[posicionNuevoCromosoma1+j] = -1;
				nuevaPoblacion[posicionNuevoCromosoma2+j] = -1;
			}
		}
		int valor, valorInicial;
		for(int k=r1; k<=r2; k++){
			valor = poblacionActual[posicionCromosoma2+k];

			valorInicial = valor;
			int pos = dondeEstaCPU(poblacionActual,valor,posicionCromosoma1);

			if((pos<r1) || (pos>r2)){
				valor = poblacionActual[posicionCromosoma1+pos];
				pos = dondeEstaCPU(poblacionActual,valor,posicionCromosoma2);
				while((pos>=r1) && (pos<=r2)){
					valor = poblacionActual[posicionCromosoma1+pos];
					pos = dondeEstaCPU(poblacionActual,valor,posicionCromosoma2);
				}
				nuevaPoblacion[posicionNuevoCromosoma1+pos] = valorInicial;
			}
		}
		for(int k=r1; k<=r2; k++){
			valor = poblacionActual[posicionCromosoma1+k];
			valorInicial = valor;
			int pos = dondeEstaCPU(poblacionActual,valor,posicionCromosoma2);
			if((pos<r1) || (pos > r2)){
				valor = poblacionActual[posicionCromosoma2+pos];
				pos = dondeEstaCPU(poblacionActual,valor,posicionCromosoma1);

				while((pos>=r1) && (pos <= r2)){
					valor = poblacionActual[posicionCromosoma2+pos];
					pos = dondeEstaCPU(poblacionActual,valor,posicionCromosoma1);
				}

				nuevaPoblacion[posicionNuevoCromosoma2+pos] = valorInicial;
			}
		}
		for(int i=0; i<cantGenesEnCromosoma; i++){
			if(nuevaPoblacion[posicionNuevoCromosoma1+i] == -1)
				nuevaPoblacion[posicionNuevoCromosoma1+i] = poblacionActual[posicionCromosoma2+i];
			if(nuevaPoblacion[posicionNuevoCromosoma2+i] == -1)
				nuevaPoblacion[posicionNuevoCromosoma2+i] = poblacionActual[posicionCromosoma1+i];
		}
	}
	else{
		for(int i=0; i<cantGenesEnCromosoma; i++){
			nuevaPoblacion[posicionNuevoCromosoma1+i] = poblacionActual[posicionCromosoma1+i];
			nuevaPoblacion[posicionNuevoCromosoma2+i] = poblacionActual[posicionCromosoma2+i];
		}
	}
}


 void cruzamientoCromosomaOX(int* poblacionActual, int* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, float probabilidad, MersenneTwister *mt, int nroPoblacion, int posActual){
	int posicionNuevoCromosoma1 = posActual * cantGenesEnCromosoma * 2 + nroPoblacion * totalGenesEnPoblacion;
	int posicionNuevoCromosoma2 = posicionNuevoCromosoma1 + cantGenesEnCromosoma;

	unsigned int prob = (mt->genrand_int32() % 101);
	if(prob < probabilidad ){
		unsigned int r1 = mt->genrand_int32() % cantGenesEnCromosoma;
		unsigned int r2 = mt->genrand_int32() % cantGenesEnCromosoma;
		if(r2 == r1){
			r2 = (r2 + 1) % cantGenesEnCromosoma;
		}

		if(r2<r1){
			unsigned int r3 = r1;
			r1 = r2;
			r2 = r3;
		}

		for(int i=0; i<cantGenesEnCromosoma; i++){
			nuevaPoblacion[posicionNuevoCromosoma1+i] = -1;
			nuevaPoblacion[posicionNuevoCromosoma2+i] = -1;
		}

		int cont1 = 0;
	    int cont2 = 0;
	    for(int i=r1; i<=r2; i++){
	    	nuevaPoblacion[posicionNuevoCromosoma1+i] = poblacionActual[posicionCromosoma1+i];
		    cont1++;

		    nuevaPoblacion[posicionNuevoCromosoma2+i] = poblacionActual[posicionCromosoma2+i];
		    cont2++;
	    }

	    int j = (r2+1) % cantGenesEnCromosoma;
		int k = (r2+1) % cantGenesEnCromosoma;

		while(cont1 < cantGenesEnCromosoma){
			if(estaPresenteCPU(poblacionActual[posicionCromosoma2 + j], nuevaPoblacion, posicionNuevoCromosoma1, posicionNuevoCromosoma1 + cantGenesEnCromosoma) == 0){
				nuevaPoblacion[posicionNuevoCromosoma1+k] = poblacionActual[posicionCromosoma2 + j];
				cont1++;
				k = (k+1) % cantGenesEnCromosoma;
			}
			j = (j+1) % cantGenesEnCromosoma;
		}

		j = (r2+1) % cantGenesEnCromosoma;
		k = (r2+1) % cantGenesEnCromosoma;
		while(cont2 < cantGenesEnCromosoma){
			if(estaPresenteCPU(poblacionActual[posicionCromosoma1 + j], nuevaPoblacion, posicionNuevoCromosoma2, posicionNuevoCromosoma2 + cantGenesEnCromosoma) == 0){
				nuevaPoblacion[posicionNuevoCromosoma2+k] = poblacionActual[posicionCromosoma1 + j];
				cont2++;
				k = (k+1) % cantGenesEnCromosoma;
			}
			j = (j+1) % cantGenesEnCromosoma;
		}
	}else{
		for(int i=0; i<cantGenesEnCromosoma; i++){
			nuevaPoblacion[posicionNuevoCromosoma1+i] = poblacionActual[posicionCromosoma1+i];
			nuevaPoblacion[posicionNuevoCromosoma2+i] = poblacionActual[posicionCromosoma2+i];
		}
	}
}


 void cruzamientoCromosomaCX(int* poblacionActual, int* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, float probabilidad,MersenneTwister *mt, int nroPoblacion, int posActual){
	int posicionNuevoCromosoma1 = posActual * cantGenesEnCromosoma * 2 + nroPoblacion * totalGenesEnPoblacion;
	int posicionNuevoCromosoma2 = posicionNuevoCromosoma1 + cantGenesEnCromosoma;

	int pos, pos2;

	unsigned int prob = (mt->genrand_int32() % 101);
	if(prob < probabilidad){
		for(int i=0; i<cantGenesEnCromosoma; i++){
			nuevaPoblacion[posicionNuevoCromosoma1+i] = -1;
			nuevaPoblacion[posicionNuevoCromosoma2+i] = -1;
		}
		int cont = 0;
		pos = posicionPrimerLibreCPU(nuevaPoblacion, posicionNuevoCromosoma1);
		while(pos != -1){
			if(cont == 0){
				while(nuevaPoblacion[posicionNuevoCromosoma1 + pos] == -1){
					nuevaPoblacion[posicionNuevoCromosoma1 + pos] = poblacionActual[posicionCromosoma1 + pos];
					pos2 = dondeEstaCPU(poblacionActual, poblacionActual[posicionCromosoma1 + pos], posicionCromosoma2);
					nuevaPoblacion[posicionNuevoCromosoma2 + pos2] = poblacionActual[posicionCromosoma1 + pos];
					pos = pos2;
				}
			}
			else{
				while(nuevaPoblacion[posicionNuevoCromosoma2 + pos] == -1){
					nuevaPoblacion[posicionNuevoCromosoma2 + pos] = poblacionActual[posicionCromosoma1 + pos];
					pos2 = dondeEstaCPU(poblacionActual, poblacionActual[posicionCromosoma1 + pos], posicionCromosoma2);
					nuevaPoblacion[posicionNuevoCromosoma1 + pos2] = poblacionActual[posicionCromosoma1 + pos];
					pos = pos2;
				}
			}
			cont = (cont +1) % 2;
			pos = posicionPrimerLibreCPU(nuevaPoblacion, posicionNuevoCromosoma1);
		}
	}
	else{
		for(int i=0; i<cantGenesEnCromosoma; i++){
			nuevaPoblacion[posicionNuevoCromosoma1+i] = poblacionActual[posicionCromosoma1+i];
			nuevaPoblacion[posicionNuevoCromosoma2+i] = poblacionActual[posicionCromosoma2+i];
		}
	}
}

/******************* Implementacion de Funciones de Mutacion *****************/

 void mutacionPermutacionCPU(int* poblacion, float probabilidad, MersenneTwister *mt, int ejecutarFuncion, int nroPoblacion, int posActual){

	if(ejecutarFuncion == 0){
		mutacionPorIntercambio(poblacion, probabilidad, mt, nroPoblacion, posActual);
	}else{
		if(ejecutarFuncion == 1){
			mutacionPorInsercionSimple(poblacion, probabilidad, mt, nroPoblacion, posActual);
		}else{
			if(ejecutarFuncion == 2){
				mutacionPorInversionIVM(poblacion, probabilidad, mt, nroPoblacion, posActual);
			}
		}
	}
}

 void mutacionPorIntercambio(int* poblacion, float probabilidad, MersenneTwister *mt, int nroPoblacion, int posActual){
	int posicionNuevoCromosoma1 = posActual * cantGenesEnCromosoma * 2 + nroPoblacion * cantCromosomasPoblacion * cantGenesEnCromosoma;
	int posicionNuevoCromosoma2 = posicionNuevoCromosoma1 + cantGenesEnCromosoma;

	unsigned int prob = (mt->genrand_int32() % 101);
	if(prob < probabilidad ){
		int r1 = (mt->genrand_int32() % cantGenesEnCromosoma);
		int r2 = ((mt->genrand_int32() + 1) % cantGenesEnCromosoma);
		if(r2 == r1){
			r2 = (r2 + 1) % cantGenesEnCromosoma;
		}
		int aux = poblacion[posicionNuevoCromosoma1 + r1];
		poblacion[posicionNuevoCromosoma1 + r1] = poblacion[posicionNuevoCromosoma1 + r2];
		poblacion[posicionNuevoCromosoma1 + r2] = aux;

	}
	prob = (mt->genrand_int32() % 101);
	if(prob < probabilidad ){
		int r1 = (mt->genrand_int32() % cantGenesEnCromosoma);
		int r2 = ((mt->genrand_int32() + 1) % cantGenesEnCromosoma);
		int aux = poblacion[posicionNuevoCromosoma2 + r1];
		poblacion[posicionNuevoCromosoma2 + r1] = poblacion[posicionNuevoCromosoma2 + r2];
		poblacion[posicionNuevoCromosoma2 + r2] = aux;
	}
}

 void mutacionPorInsercionSimple(int* poblacion, float probabilidad, MersenneTwister *mt, int nroPoblacion, int posActual){
	int posicionNuevoCromosoma1 = posActual * cantGenesEnCromosoma * 2 + nroPoblacion * cantCromosomasPoblacion * cantGenesEnCromosoma;
	int posicionNuevoCromosoma2 = posicionNuevoCromosoma1 + cantGenesEnCromosoma;
	unsigned int r3;
	int aux;
	unsigned int r1, r2;

	unsigned int prob = (mt->genrand_int32() % 101);
	if(prob < probabilidad ){
		r1 = mt->genrand_int32() % cantGenesEnCromosoma;
		r2 = mt->genrand_int32() % cantGenesEnCromosoma;
		if(r2 == r1){
			r2 = (r2 + 1) % cantGenesEnCromosoma;
		}
		if(r2 < r1){
			r3 = r1;
			r1 = r2;
			r2 = r3;
		}
		aux = poblacion[posicionNuevoCromosoma1+r2];
	    for(int i=r2; i>r1+1; i--){
	    	poblacion[posicionNuevoCromosoma1+i] = poblacion[posicionNuevoCromosoma1+i-1];
	    }
	    poblacion[posicionNuevoCromosoma1+r1+1] = aux;
	}
	prob = (mt->genrand_int32() % 101);
	if(prob < probabilidad ){
		r1 =(mt->genrand_int32() + 1) % cantGenesEnCromosoma;
		r2 =(mt->genrand_int32() + 1) % cantGenesEnCromosoma;
		if(r2 == r1){
			r2 = (r2 + 1) % cantGenesEnCromosoma;
		}
		if(r2 < r1){
			r3 = r1;
			r1 = r2;
			r2 = r3;
		}
		aux = poblacion[posicionNuevoCromosoma2+r2];
		for(int i=r2; i>r1+1; i--){
			poblacion[posicionNuevoCromosoma2+i] = poblacion[posicionNuevoCromosoma2+i-1];
		}
		poblacion[posicionNuevoCromosoma2+r1+1] = aux;
	}
}


 void mutacionPorInversionIVM(int* poblacion, float probabilidad, MersenneTwister *mt, int nroPoblacion, int posActual){
	int posicionNuevoCromosoma1 = posActual * cantGenesEnCromosoma * 2 + nroPoblacion * cantCromosomasPoblacion * cantGenesEnCromosoma;
	int posicionNuevoCromosoma2 = posicionNuevoCromosoma1 + cantGenesEnCromosoma;
	unsigned int r3;
	unsigned int r1, r2;
	int tam, pos2;
	unsigned int prob = (mt->genrand_int32() % 101);
	if(prob < probabilidad ){
		r1 = mt->genrand_int32() % cantGenesEnCromosoma;
		r2 = mt->genrand_int32() % cantGenesEnCromosoma;
		if(r2 == r1){
			r2 = (r2 + 1) % cantGenesEnCromosoma;
		}
		if(r2 < r1){
			r3 = r1;
			r1 = r2;
			r2 = r3;
		}
		tam = r2-r1+1;
		pos2 = r2;
		int swap;
		for(int i=r1; i<r1 + tam/2; i++){
			swap = poblacion[posicionNuevoCromosoma1+i];
			poblacion[posicionNuevoCromosoma1+i] = poblacion[posicionNuevoCromosoma1+pos2];
			poblacion[posicionNuevoCromosoma1+pos2] = swap;
			pos2--;
		}
	}

	prob = (mt->genrand_int32() % 101);
	if(prob < probabilidad ){
		r1 =(mt->genrand_int32() + 1) % cantGenesEnCromosoma;
		r2 =(mt->genrand_int32() + 1) % cantGenesEnCromosoma;
		if(r2 == r1){
			r2 = (r2 + 1) % cantGenesEnCromosoma;
		}
		if(r2 < r1){
			r3 = r1;
			r1 = r2;
			r2 = r3;
		}
		tam = r2-r1+1;
		pos2 = r2;
		int swap;
		for(int i=r1; i<r1 + tam/2; i++){
			swap = poblacion[posicionNuevoCromosoma2+i];
			poblacion[posicionNuevoCromosoma2+i] = poblacion[posicionNuevoCromosoma2+pos2];
			poblacion[posicionNuevoCromosoma2+pos2] = swap;
			pos2--;
		}
	}
}

/******************* Implementacion de Funciones de fitness *****************/

 void asignarFitnessPermutacionCPU(int* nuevaPoblacion, float* nuevoVectorFitness, int nroPoblacion, int posActual, float* fitnessOrdenado, float* matrizCostosHost){
	//Obtengo posicion del primer cromosoma.
	int posicionCromosoma = posActual * cantGenesEnCromosoma * 2 + nroPoblacion * totalGenesEnPoblacion;
	int posicionFitness = posActual * 2 + nroPoblacion * cantCromosomasPoblacion;
	nuevoVectorFitness[posicionFitness] = calcularFitnessPermutacionCPU(nuevaPoblacion, posicionCromosoma, matrizCostosHost);
	nuevoVectorFitness[posicionFitness + 1] = calcularFitnessPermutacionCPU(nuevaPoblacion, posicionCromosoma + cantGenesEnCromosoma, matrizCostosHost);

	fitnessOrdenado[posicionFitness] = nuevoVectorFitness[posicionFitness];
	fitnessOrdenado[posicionFitness + 1] = nuevoVectorFitness[posicionFitness + 1];
}


 /******************** Implementacion de Funciones de reemplazo **************/

void reemplazoPermutacionCPU(int * poblacionActual, int * nuevaPoblacion, int * poblacionActualAcambiar, int * nuevaPoblacionAcambiar, float * fitnessActual, float* nuevoFitness, int ejecutarFuncion, int nroPoblacion, int posActual){
 	if(ejecutarFuncion == 1){
 		reemplazoElitistaPermutacionCPU(poblacionActual, nuevaPoblacion, poblacionActualAcambiar, nuevaPoblacionAcambiar, fitnessActual, nuevoFitness, nroPoblacion, posActual);
 	}
 }

void reemplazoElitistaPermutacionCPU(int* poblacionActual, int* nuevaPoblacion, int* poblacionActualAcambiar, int* nuevaPoblacionAcambiar, float * fitnessActual, float* nuevoFitness, int nroPoblacion, int posActual){
 	if(posActual == cantCromosomasPoblacion/2 - 1){
 		int cantAremplazar = (int) (porcentajeElitismo * cantCromosomasPoblacion / 100);
 		float peor = INT_MAX;
 		float mejor = INT_MIN;
 		int posPeor = -1;
 		int posMejor = -1;
 		int posFitness = nroPoblacion * cantCromosomasPoblacion;
 		for(int i = 0; i < cantAremplazar; i++){
 			if(fitnessActual[posFitness+i] < peor){
 				peor = fitnessActual[posFitness+i];
 				posPeor = i;
 			}

 			if(nuevoFitness[posFitness+i] > mejor){
 				mejor = nuevoFitness[posFitness+i];
 				posMejor = i;
 			}

 			poblacionActualAcambiar[posFitness+i] = 1;
 			nuevaPoblacionAcambiar[posFitness+i] = 1;
 		}

 		for(int i = cantAremplazar; i < cantCromosomasPoblacion; i++){
 			if(fitnessActual[posFitness+i] > peor){
 				poblacionActualAcambiar[posFitness+i] = 1;
 				poblacionActualAcambiar[posFitness+posPeor] = 0;
 				peor = INT_MAX;
 				posPeor = -1;
 				for(int j = 0; j <= i; j++){
 					if(poblacionActualAcambiar[posFitness+j] == 1){
 						if(fitnessActual[posFitness+j] < peor){
 							peor = fitnessActual[posFitness+j];
 							posPeor = j;
 						}
 					}
 				}
 			}
 			else{
 				poblacionActualAcambiar[posFitness+i] = 0;
 			}

 			if(nuevoFitness[posFitness+i] < mejor){
 				nuevaPoblacionAcambiar[posFitness+i] = 1;
 				nuevaPoblacionAcambiar[posFitness + posMejor] = 0;
 				mejor = INT_MIN;
 				posMejor = -1;
 				for(int j = 0; j <= i; j++){
 					if(nuevaPoblacionAcambiar[posFitness+j] == 1){
 						if(nuevoFitness[posFitness+j] > mejor){
 							mejor = nuevoFitness[posFitness+j];
 							posMejor = j;
 						}
 					}
 				}
 			}
 			else{
 				nuevaPoblacionAcambiar[posFitness+i] = 0;
 			}
 		}

 		for(int i = 0; i < cantAremplazar; i++){
 			int j = 0;
 			while(poblacionActualAcambiar[posFitness+j] == 0){
 				j++;
 			}

 			int k = 0;
 			while(nuevaPoblacionAcambiar[posFitness+k] == 0){
 				k++;
 			}

 			nuevoFitness[posFitness+k] = fitnessActual[posFitness+j];
 			int posNuevoCromosoma = nroPoblacion * cantCromosomasPoblacion * cantGenesEnCromosoma + k * cantGenesEnCromosoma;
 			int posCromosomaActual = nroPoblacion * cantCromosomasPoblacion * cantGenesEnCromosoma + j * cantGenesEnCromosoma;
 			for(int h = 0; h < cantGenesEnCromosoma; h++){
 				nuevaPoblacion[posNuevoCromosoma+h] = poblacionActual[posCromosomaActual+h];
 			}
 			poblacionActualAcambiar[posFitness+j] = 0;
 			nuevaPoblacionAcambiar[posFitness+k] = 0;
 		}
 	}
 }


/******************* Implementacion de Funciones del host *****************/

 void imprimirPermutacionCPU(float* fitnessHost, int tamVectorFitness, int* poblacionHost, int tamVectorPoblacion, int iteracion){
	printf("\nPOBLACION EN CPU (iteracion %d):\n",iteracion);
  	for(int i = 0; i<totalGenesCPU; i++){
  		if(i%cantGenesEnCromosoma == 0){
			printf("\n");
  		}
  		printf("%i ", poblacionHost[i]);
  	}
	printf("\n\nFITNESS EN CPU:\n\n");
  	for(int i = 0; i<cantCromosomasPoblacion * cantidadPoblacionesCPU; i++){
  		printf("%f ", fitnessHost[i]);
  	}
	printf("\n\n");

 }

 
void imprimirMejorIndividuoPermutacionCPU(float* mejorFitnessIndividuoCPU, int tamVectorFitnessMejoresIndividuosCPU, int* mejorIndividuoCPU, int tamVectorMejoresIndividuosCPU, int iteracion){
	printf("\nMEJOR INDIVIDUO POR POBLACION EN CPU (iteracion %d):\n",iteracion);
 	for(int i = 0; i < cantGenesEnCromosoma * cantidadPoblacionesCPU; i++){
 		if(i%cantGenesEnCromosoma == 0){
			printf("\n");
		}
 		printf("%i ", mejorIndividuoCPU[i]);
 	}
	printf("\n\nFITNESS MEJOR INDIVIDUO POR POBLACION EN CPU:\n\n");
 	for(int i = 0; i< cantidadPoblacionesCPU; i++){
		printf("%f ", mejorFitnessIndividuoCPU[i]);
	}
	printf("\n\n");
}

 void representacionPermutacionCPU(int *poblacionActualHost, int *nuevaPoblacionHost, float *fitnessActualHost, float *nuevoFitnessHost,int * poblacionActualAcambiar, int *  nuevaPoblacionAcambiar, int * individuosAMigrarHostCPU, MersenneTwister* mt, int iteracion, int tamVectorPoblacion, int tamVectorFitness, int *individuoAMigrarCPUaGPUhost, float* fitnessOrdenadoHostCPU, int* indicesFitnessOrdenadoHostCPU, float* probabilidadRuletaHostCPU, float* fitnessTotalParaRuletaHostCPU, float* mejorFitnessIndividuoCPU, int tamVectorFitnessMejoresIndividuosCPU, int* mejorIndividuoCPU, int tamVectorMejoresIndividuosCPU, float* matrizCostosHost){
 	int nroPoblacion = 0;
 	for(int i = 0; i < cantidadPoblacionesCPU*cantCromosomasPoblacion/2; i++){
 		nroPoblacion = i/(cantCromosomasPoblacion/2);
 		if((iteracion % 2) == 0){
 			mainKernelPermutacionCPU(poblacionActualHost, nuevaPoblacionHost, fitnessActualHost, nuevoFitnessHost, poblacionActualAcambiar, nuevaPoblacionAcambiar, individuosAMigrarHostCPU, mt, nroPoblacion, i%(cantCromosomasPoblacion/2), individuoAMigrarCPUaGPUhost, fitnessOrdenadoHostCPU, indicesFitnessOrdenadoHostCPU, iteracion, probabilidadRuletaHostCPU, fitnessTotalParaRuletaHostCPU, mejorIndividuoCPU, mejorFitnessIndividuoCPU, matrizCostosHost);
 		}else{
 			mainKernelPermutacionCPU(nuevaPoblacionHost, poblacionActualHost, nuevoFitnessHost, fitnessActualHost, poblacionActualAcambiar, nuevaPoblacionAcambiar, individuosAMigrarHostCPU, mt, nroPoblacion, i%(cantCromosomasPoblacion/2), individuoAMigrarCPUaGPUhost, fitnessOrdenadoHostCPU, indicesFitnessOrdenadoHostCPU, iteracion, probabilidadRuletaHostCPU, fitnessTotalParaRuletaHostCPU, mejorIndividuoCPU, mejorFitnessIndividuoCPU, matrizCostosHost);
 		}
 	}

 	//Imprimo solo en la ultima iteracion o si esta en modo debug imprimo siempre
 	if((iteracion == numeroGeneraciones - 1) || (modoDebug == 1)){
 		if((iteracion % 2) == 0){
 			if(imprimirPoblacionEntera == 1)
				imprimirPermutacionCPU(nuevoFitnessHost, tamVectorFitness, nuevaPoblacionHost, tamVectorPoblacion, iteracion);
			else
				imprimirMejorIndividuoPermutacionCPU(mejorFitnessIndividuoCPU, tamVectorFitnessMejoresIndividuosCPU, mejorIndividuoCPU, tamVectorMejoresIndividuosCPU, iteracion);
 		}else{
 			if(imprimirPoblacionEntera == 1)
				imprimirPermutacionCPU(fitnessActualHost, tamVectorFitness, poblacionActualHost, tamVectorPoblacion, iteracion);
			else
				imprimirMejorIndividuoPermutacionCPU(mejorFitnessIndividuoCPU, tamVectorFitnessMejoresIndividuosCPU, mejorIndividuoCPU, tamVectorMejoresIndividuosCPU, iteracion);
 		}
 	}
}

#endif
